#ifndef T_AUTOMA_MODULES_SLAVES_ROBOTIC_PROTOTYPE_HPP
#define T_AUTOMA_MODULES_SLAVES_ROBOTIC_PROTOTYPE_HPP

#include "MotorizedPrototype.hpp"

struct RoboticSlave : MotorizedSlave {
	struct State {
		// reference GRBL master
		char* action = nullptr;
		float* curXYZABC = nullptr;
		float* targetXYZABC = nullptr;
		float* progress = nullptr;
		int* limits = nullptr;
		std::string* sensors;
		int numberOfParsedAxis = 6;

		// not used yet
		uint32_t lastCommandMask = 0;

		std::string print();
		bool isRunning();
		bool isIdle();
		bool isUnkown();
	}_state;

	// update in 
	//struct SceneViewState {
	//	// if reference available, use reference + offset to update posXYZ
	//	SceneViewState* posRefXYZ[3] = { nullptr, nullptr, nullptr };
	//	float posRefOffset[3] = { 0,0,0 };

	//	// if not referenced, use 
	//	float posXYZ[3] = { 0,0,0 };
	//	float posOffsetFromPhysical[3] = { 0,0,0 };
	//	int posOfPhysicalAxis[3] = { -1,-1,-1 };
	//	// + if follow global setting, right/forward/high
	//	// 0 if stationary
	//	int dirOfPhysical[3] = { 0,0,0 };

	//	// to ensure refernce updated first
	//	uint64_t updateIndexXYZ[3] = { 0,0,0 };

	//	void makeStationary();
	//}_sceneState;

	struct Misc {
		float axialErrorAllowance = 0.01;
		void* slave = nullptr;
	}_misc;

	RoboticSlave();
	RoboticSlave(GeneralSlave* ref);
	~RoboticSlave();

	int roboInit(GeneralSlave* ref);
	void roboInit();
	void roboRelease();

	int setOfflineSimu(int flag, float simuAccRateMMPerSecSec);
	//int simuStart(float rate);
	bool isSimu();

	// obsolete
	int init(const wchar_t* hwid);
	//int isConnected();
	void setCoordinator(GeneralSlave* coordinatorIn) override;

	int setConfig(std::vector<std::string>& keys, std::vector<std::string>& vals);
	int saveConfig(std::vector<std::string>& keys, std::vector<std::string>& vals);
	int readOut(uint64_t readIndex = 0) override;
	//int updateSceneState(uint64_t index);
	void setRatePercent(int rate);

	int setHereOrigin();
	int home();
	int homeAxis(int coordMask);
	int homeAxisByDir(std::wstring dir);
	int homeAxisByCodes(std::wstring codes);
	// start with 1
	int homeAxisesByIndex(std::vector<int> indexes);
	int moveToPosition(int positionMask, std::vector<float> coordinates, float rateMMPerMin = 0);
	int queueMoveToPosition(std::wstring command);

	// to target position
	int moveToTargetPosition(int positionMask, float rateMMPerMin = 0);
	int moveByDistance(int distanceMask, std::vector<float> distances, float rateMMPerMin = 0);
	// axis index (0~5) 
	//int moveInAxis(int axis, int dir, float distMM);
	int moveInDir(std::wstring dir, float distMM, float mmPerMin = -1);
	int moveAxisesInDir(std::vector<std::wstring> axisCodes,
		std::wstring dir,
		std::vector<float> distMMs,
		float mmPerMin = -1);

	// not all movements allowed, i.e. axises are synced but here distances are different
	std::wstring getMoveByDistanceAllowed(int distanceMask, std::vector<float> distances);

	int completeHoming(int coordMask);

	int confirmPosition(int positionMask, std::vector<float> coordinates);

	int executeExAxisCommand(std::vector<std::wstring> axisCodes,
		std::wstring command, std::wstring option);

	int watchUntilComplete();;

	// such as channel_number
	std::wstring getTypeWithDetail() override;
	std::wstring getActionStr() override;
	std::wstring getCurCoordStr() override;
	std::wstring getTargetCoordStr() override;
	virtual std::wstring getLimitedStr() override;
	virtual std::wstring getSensorsStr() override;
	// use with caution
	float getCurCoord(int axis) override;
	std::wstring formatCoordStr(int mask, std::vector<float>coordinates);

	virtual int stop(int hardStop) override;
	virtual int unlock();
	int miscFunc(std::wstring command, std::wstring& reply);

	int confirmExeStatusWithExpectation();

	// since we use another layer of control to send/receive data, we wrap the callbacks here
	// see SerialCallbackMP
	//static void controlSerialSendCallback(int index, bool success, const void* log, int logLen, const void* data, int dataLen);
	//static void controlSerialReceiveCallback(int index, bool success, const void* log, int logLen, const void* data, int dataLen);

private:
	const int SLAVE_TIME_OUT_MS = 100;

	void initDefaultMeta();
	void simuInit();

	void updateAfterConnectAny(HandShakeProtocol* protocol) override;
	void initCoreAndLinkVars();
	void linkCore2SerialComm(void* handle, int ownHandle);

protected:
	// if func name matches is listed here, it is used as a axis (in config order)
	virtual std::set<std::wstring> getAxialFuncs();
};

#endif